package com.deli.servlet;

import com.deli.anno.*;
import com.deli.controller.TestController;
import com.deli.handleradapter.DeliHandlerAdapter;
import com.deli.handleradapter.HandlerAdapterService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Author: xudeli
 * @CreateDate: 2019/8/24 21:42
 **/
public class DeliDispatcherServlet extends HttpServlet {

    private List<String> classNames = new ArrayList<String>();
    private Map<String, Object> beans = new HashMap<String, Object>();
    private Map<String, Object> headlerMap = new HashMap<String, Object>();

    public void init(ServletConfig config) throws ServletException {

        scanPackage("com.deli");
        doInstance();
        doAutoWried();
        urlMapping();
    }

    private void urlMapping() {
        //取实例化bean
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //取实例化bean
            Object instance = entry.getValue();
            //根据实例拿到class类
            Class<?> clazz = instance.getClass();
            //找到DeliContrller注解类
            if (clazz.isAnnotationPresent(DeliController.class)) {
                DeliRequestMapping clazzAnnotation = clazz.getAnnotation(DeliRequestMapping.class);
                String clazzValue = clazzAnnotation.value();
                //拿到所有的方法
                Method[] methods = clazz.getDeclaredMethods();
                //遍历所有的方法
                for (Method method : methods) {
                    //找到DeliRequestMapping注解类
                    if (method.isAnnotationPresent(DeliRequestMapping.class)) {
                        DeliRequestMapping methodAnnotation = method.getAnnotation(DeliRequestMapping.class);
                        String methodValue = methodAnnotation.value();
                        //添加到headlerMap中
                        headlerMap.put(clazzValue + methodValue, method);
                    }
                }
            } else {
                continue;
            }
        }
    }

    private void doAutoWried() {
        if (beans.isEmpty()) {
            return;
        }
        //取实例化bean
        for (Map.Entry<String, Object> entry : beans.entrySet()) {
            //取实例化bean
            Object instance = entry.getValue();
            //根据实例拿到class类
            Class<?> clazz = instance.getClass();
            //找到DeliContrller注解类
            if (clazz.isAnnotationPresent(DeliController.class)) {
                //获取所有的成员属性
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    //注解上是否有DeliAutoWried注解
                    if (field.isAnnotationPresent(DeliAutoWried.class)) {
                        //找到DeliAutoWried注解类
                        DeliAutoWried annotation = field.getAnnotation(DeliAutoWried.class);
                        //拿到注解类里设置的value值，也是我们在doInstance设置的key值
                        String key = annotation.value().trim();

                        if ("".equals(key.trim())) {
                            key= field.getType().getName();
                        }else{
                            key = annotation.value();

                        }
                        //成员变量里是private修饰的，所以要取得下权限
                        field.setAccessible(true);
                        //给成员变量实例化
                        try {
                            field.set(instance, beans.get(key));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    } else {
                        continue;
                    }
                }
            } else {
                continue;
            }
        }
    }

    private void doInstance() {
        if (classNames.isEmpty()) {
            return;
        }
        for (String className : classNames) {
            String cn = className.replaceAll("\\.class", "");
            try {
                Class<?> clazz = Class.forName(cn);
                //类上面是否用了注解
                if (clazz.isAnnotationPresent(DeliController.class)) {
                    Object instance = clazz.newInstance();
                    DeliRequestMapping annotation = clazz.getAnnotation(DeliRequestMapping.class);
                    String key = annotation.value();
                    beans.put(key, instance);
                } else if (clazz.isAnnotationPresent(DeliService.class)) {
                    Object instance1 = clazz.newInstance();
                    DeliService annotation = clazz.getAnnotation(DeliService.class);
                     String key1 = annotation.value();
                    beans.put(key1, instance1);
                } else {
                    continue;
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    private void scanPackage(String basePackage) {
        //首先找到计算机文件的路径
        //因为电脑系统的路径为/，而项目的路径为.所以要转换下
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        String urlFile = url.getFile();
        File file = new File(urlFile);
        String[] files = file.list();
        for (String f : files) {
            File filePath = new File(urlFile + f);
            if (filePath.isDirectory()) {
                scanPackage(basePackage + "." + f);
            } else {
                //需要路径为com.deli.xxx
                classNames.add(basePackage + "." + filePath.getName());
            }
        }

    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        this.doPost(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        //路径为项目名+com/deli/xxx
        String uri = req.getRequestURI();


        //项目名
        String contextPath = req.getContextPath();
        //将项目名替换掉
        String path = uri.replace(contextPath, "");
        if (headlerMap.containsKey(path)) {
            try {
                Method method = (Method) headlerMap.get(path);
                TestController instance = (TestController) beans.get("/" + path.split("/")[1]);
                //Object[] args = hand(req, resp, method);
                // 处理参数
                HandlerAdapterService ha = (HandlerAdapterService) beans.get("deliHandlerAdapter");
                Object[] args = ha.handle(req, resp, method, beans);
                method.invoke(instance, args);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }




    }

    public static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) {
        //拿到当前方法中
        Class<?>[] parameterTypes = method.getParameterTypes();
        System.out.println(parameterTypes.length);
        Object[] args = new Object[parameterTypes.length];
        int args_index = 0;
        int index = 0;
        for (Class<?> parameterType : parameterTypes) {
            if (ServletRequest.class.isAssignableFrom(parameterType)) {
                args[args_index++] = request;
            }
            if (ServletResponse.class.isAssignableFrom(parameterType)) {
                args[args_index++] = response;
            }
            Annotation[] annotations = method.getParameterAnnotations()[index];
            if (annotations.length > 0) {
                for (Annotation annotation : annotations) {
                    if (DeliRequestParam.class.isAssignableFrom(annotation.getClass())) {
                        DeliRequestParam drp = (DeliRequestParam) annotation;
                        args[args_index++] = request.getParameter(drp.value());
                    }
                }
            }
            index++;
        }
        return args;
    }


}
